/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2018 Live Networks, Inc.  All rights reserved.
// Media Sinks
// C++ header

#ifndef _MEDIA_SINK_HH
#define _MEDIA_SINK_HH

#ifndef _FRAMED_SOURCE_HH

#include "FramedSource.hh"

#endif

class MediaSink : public Medium {
public:
    static Boolean lookupByName(UsageEnvironment &env, char const *sinkName,
                                MediaSink *&resultSink);

    typedef void (afterPlayingFunc)(void *clientData);

    Boolean startPlaying(MediaSource &source,
                         afterPlayingFunc *afterFunc,
                         void *afterClientData);

    virtual void stopPlaying();

    // Test for specific types of sink:
    virtual Boolean isRTPSink() const;

    FramedSource *source() const { return fSource; }

protected:
    MediaSink(UsageEnvironment &env); // abstract base class
    virtual ~MediaSink();

    virtual Boolean sourceIsCompatibleWithUs(MediaSource &source);

    // called by startPlaying()
    virtual Boolean continuePlaying() = 0;
    // called by startPlaying()

    static void onSourceClosure(void *clientData); // can be used in "getNextFrame()" calls
    void onSourceClosure();
    // should be called (on ourselves) by continuePlaying() when it
    // discovers that the source we're playing from has closed.

    FramedSource *fSource;

private:
    // redefined virtual functions:
    virtual Boolean isSink() const;

private:
    // The following fields are used when we're being played:
    afterPlayingFunc *fAfterFunc;
    void *fAfterClientData;
};

// A data structure that a sink may use for an output packet:
class OutPacketBuffer {
public:
    OutPacketBuffer(unsigned preferredPacketSize, unsigned maxPacketSize,
                    unsigned maxBufferSize = 0);

    // if "maxBufferSize" is >0, use it - instead of "maxSize" to compute the buffer size
    ~OutPacketBuffer();

    static unsigned maxSize;

    static void increaseMaxSizeTo(unsigned newMaxSize) {
        if (newMaxSize > OutPacketBuffer::maxSize)OutPacketBuffer::maxSize = newMaxSize;
    }

    unsigned char *curPtr() const { return &fBuf[fPacketStart + fCurOffset]; }

    unsigned totalBytesAvailable() const {
        return fLimit - (fPacketStart + fCurOffset);
    }

    unsigned totalBufferSize() const { return fLimit; }

    unsigned char *packet() const { return &fBuf[fPacketStart]; }

    unsigned curPacketSize() const { return fCurOffset; }

    void increment(unsigned numBytes) { fCurOffset += numBytes; }

    void enqueue(unsigned char const *from, unsigned numBytes);

    void enqueueWord(u_int32_t word);

    void insert(unsigned char const *from, unsigned numBytes, unsigned toPosition);

    void insertWord(u_int32_t word, unsigned toPosition);

    void extract(unsigned char *to, unsigned numBytes, unsigned fromPosition);

    u_int32_t extractWord(unsigned fromPosition);

    void skipBytes(unsigned numBytes);

    Boolean isPreferredSize() const { return fCurOffset >= fPreferred; }

    Boolean wouldOverflow(unsigned numBytes) const {
        return (fCurOffset + numBytes) > fMax;
    }

    unsigned numOverflowBytes(unsigned numBytes) const {
        return (fCurOffset + numBytes) - fMax;
    }

    Boolean isTooBigForAPacket(unsigned numBytes) const {
        return numBytes > fMax;
    }

    void setOverflowData(unsigned overflowDataOffset,
                         unsigned overflowDataSize,
                         struct timeval const &presentationTime,
                         unsigned durationInMicroseconds);

    unsigned overflowDataSize() const { return fOverflowDataSize; }

    struct timeval overflowPresentationTime() const { return fOverflowPresentationTime; }

    unsigned overflowDurationInMicroseconds() const { return fOverflowDurationInMicroseconds; }

    Boolean haveOverflowData() const { return fOverflowDataSize > 0; }

    void useOverflowData();

    void adjustPacketStart(unsigned numBytes);

    void resetPacketStart();

    void resetOffset() { fCurOffset = 0; }

    void resetOverflowData() { fOverflowDataOffset = fOverflowDataSize = 0; }

private:
    unsigned fPacketStart, fCurOffset, fPreferred, fMax, fLimit;
    unsigned char *fBuf;

    unsigned fOverflowDataOffset, fOverflowDataSize;
    struct timeval fOverflowPresentationTime;
    unsigned fOverflowDurationInMicroseconds;
};

#endif
